from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.contrib.auth.hashers import check_password, make_password
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin
from rest_framework.response import Response
from rest_framework.status import HTTP_400_BAD_REQUEST
from rest_framework import serializers
from rest_framework.validators import ValidationError

from oplatform.serializers.configuration import UserSerializer, MenuSerializer, MenuTreeSerializer
from utils.encryption import cal_sha1_string, make_token
from utils.rest.decorator import action_name
from utils.rest.viewsets import GenericViewSet
from utils.time import get_current_time_by_microsecond
from ..models import User, Menu
from django.conf import settings


# noinspection PyUnusedLocal
class UserViewSet(GenericViewSet,
                  CreateModelMixin,
                  ListModelMixin,
                  RetrieveModelMixin,
                  DestroyModelMixin,
                  ):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = (OrderingFilter,)

    @action_name('用户登录', user_default=True)
    @action(methods=['post'], detail=False)
    def login(self, request):
        username = request.data.get("username")
        password = request.data.get("password")
        password = cal_sha1_string(password)
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            return Response({'msg': '用户不存在'}, status=HTTP_400_BAD_REQUEST)
        if not check_password(password, user.password):
            return Response({'msg': '密码错误'}, status=HTTP_400_BAD_REQUEST)
        self.request.user = user
        token = make_token()
        login_token_key = settings.REDIS_LOGIN_TOKEN_KEY.format(token=token)
        login_user_key = settings.REDIS_LOGIN_USER_KEY.format(user_id=user.id)
        data = {
            'id': user.id,
            'username': user.username,
            'token': token,
            'client_host': self.get_client_ip(),
            'create_timestamp': get_current_time_by_microsecond()
        }
        self.redis.hmset(login_token_key, data)
        self.redis.expire(login_token_key, 3600)
        self.redis.hmset(login_user_key, {token: data})
        layer = get_channel_layer()
        async_to_sync(layer.group_send)(
            settings.LOGIN_CHANNEL_GROUP_NAME.format(username=user.username),
            {'type': 'another.login', 'content': data}
        )
        return Response({'token': token, 'msg': '认证成功'})

    @action_name('获取当前用户认证状态', user_default=True)
    @action(methods=["get"], detail=False)
    def is_authenticated(self, request):
        return Response({'status': 'success'})

    @action_name('注销用户登录', user_default=True)
    @action(methods=['get'], detail=False)
    def sign_out(self, request):
        auth_token = request.auth
        self.redis.delete(settings.REDIS_LOGIN_TOKEN_KEY.format(token=auth_token))
        self.redis.hdel(settings.REDIS_LOGIN_USER_KEY.format(user_id=request.user.id), auth_token)
        return Response({'status': 'success'})

    @action_name('修改当前用户密码', user_default=True)
    @action(methods=['post'], detail=False)
    def change_pwd(self, request):
        user_id = request.user.id
        user = User.objects.get(pk=1)
        password = request.data.get("password")
        new_password = request.data.get("newPassword")
        if not password:
            raise ValidationError({'password': '密码不能为空'})
        password = cal_sha1_string(password)
        if not check_password(password, user.password):
            raise serializers.ValidationError({'msg': '密码校验失败，请重新输入'})
        user.password = make_password(cal_sha1_string(new_password))
        user.save()
        return Response({'status': 'success'})

    @action_name('获取当前用户信息', user_default=True)
    @action(methods=['get'], detail=False)
    def info(self, request):
        serializer = self.get_serializer(instance=User.objects.get(id=1))
        return Response(serializer.data)

    @action_name('获取任意用户菜单树')
    @action(methods=['get'], detail=True)
    def menu_tree(self, request, *args, **kwargs):
        user = self.get_object()
        serializer = MenuSerializer(instance=Menu.objects.all(), many=True)
        return Response(self.get_user_menu_tree())

    @action_name('获取当前用户菜单树', user_default=True)
    @action(methods=['get'], detail=False)
    def my_menu_tree(self, request, *args, **kwargs):
        return Response(self.get_user_menu_tree())

    @staticmethod
    def get_user_menu_tree():
        user_root_menus = Menu.objects.filter(father=None)
        list_serializer = MenuSerializer(instance=Menu.objects.all(), many=True)
        serializer = MenuTreeSerializer(instance=user_root_menus, many=True)
        return {'list': list_serializer.data, 'tree': serializer.data}

